home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
9675
/
9675.xpi
/
chrome
/
content
/
simpletimer.js
< prev
Wrap
Text File
|
2009-11-29
|
95KB
|
2,389 lines
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Simple Timer.
*
* The Initial Developer of the Original Code is
* George Bradt.
*
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* George Bradt.
* Tsprajna, localization of app description.
*
* Credits:
* clockBlue16.png, clockRed16.png - Mark James at http://www.famfamfam.com/lab/icons/silk/
* clock32.png, clock48.png - David Vignoni at http://www.icon-king.com
* textbox style (adapted) - Regis Caspar at http://caspar.regis.free.fr/timespinner/
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var SimpleTimer = {
prefs: null,
popupNotify: false,
audioNotify: false,
dialogNotify: false,
repeatAudio: 0,
repeatMax: 1,
audioFile: 0,
displayIn: 0,
onStartup: 0,
eventLogging: true,
eventLogPath: "",
deleteLogEntries: 0,
leftClick: 0,
disablePauseResume: false,
tooltip: 2,
clockType: 0,
showSecs: false,
clock24Hr: false,
dateFormat: 0,
countdownSeconds: 0,
countdownMinutes: 0,
countdownHours: 0,
countdownMilli: 0,
countdownRecurring: false,
countdowns: "",
favCountdowns: "",
notifyRecur: "",
countdownTable: [],
countdownTimer: {timeDisplay: "",
recurring: false,
description: "",
timeMilli: 0,
timeFinishedMilli: 0,
paused: false
},
notifyTable: [],
notification: {completed: false,
recurring: false,
timeDisplay: "",
description: "",
time24hr: "",
timeMilli: 0,
day: -1,
url: ""
},
currNotifyIndex: 0,
completedIndices: [],
currNotifyInterval: 0,
paused: false,
timerTooltipID: 0,
timerID: 0,
notifyID: 0,
repeatAudioId: 0,
repeatMaxId: 0,
startTime: null,
startBrowserTime: null,
elapsedSecs: 0,
elapsedMins: 0,
elapsedHours: 0,
elapsedMilli: 0,
remainingSecs: 0,
remainingMins: 0,
remainingHours: 0,
remainingMilli: 0,
clockInProgress: false,
countdownInProgress: false,
countupInProgress: false,
savedCountupTime: "",
barContainerElement: "",
barImageElement: "",
barLabelElement: "",
buttonOnToolbar: false,
customAudioFile: null,
customAudioPath: "",
audioObjectSupport: false,
audioObject: null,
usingAudioObject: false,
audioDuration: 0,
// Initialize the extension.
startup: function() {
var strbundle = document.getElementById("simtim-strings");
try {
// Check if this browser supports audio object (>= ff 3.5).
this.audioObject = new Audio();
this.audioObjectSupport = true;
// "pause" and "abort" listeners for debugging purposes.
this.audioObject.addEventListener("loadedmetadata", function(event) { SimpleTimer.audioLoadedMetaData(event); }, false);
this.audioObject.addEventListener("ended", function(event) { SimpleTimer.audioPlaybackEnded(event); }, false);
this.audioObject.addEventListener("pause", function(event) { SimpleTimer.audioPlaybackPause(event); }, false);
this.audioObject.addEventListener("abort", function(event) { SimpleTimer.audioPlaybackAbort(event); }, false);
this.audioObject.addEventListener("error", function(event) { SimpleTimer.audioPlaybackError(event); }, false);
}
catch(e) {
this.audioObjectSupport = false;
}
try {
// Register to receive notification of windows opening (specifically "Customize Toolbar"),
// to keep an eye on whereabouts of toolbar button.
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "domwindowopened", false);
}
catch(e) {
alert(strbundle.getString("alert.error.observer.service") +
"\n" + e.message);
}
try {
// Register to receive notifications of preference changes.
this.prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService).
getBranch("extensions.simpletimer@grbradt.org.");
this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
this.prefs.addObserver("", this, false);
this.popupNotify = this.prefs.getBoolPref("popupNotify");
this.audioNotify = this.prefs.getBoolPref("audioNotify");
this.dialogNotify = this.prefs.getBoolPref("dialogNotify");
this.repeatAudio = this.prefs.getIntPref("repeatAudio");
this.repeatMax = this.prefs.getIntPref("repeatMax");
this.audioFile = this.prefs.getIntPref("audioFile");
this.displayIn = this.prefs.getIntPref("displayIn");
this.onStartup = this.prefs.getIntPref("onStartup");
this.eventLogging = this.prefs.getBoolPref("eventLogging");
this.deleteLogEntries = this.prefs.getIntPref("deleteLogEntries");
this.leftClick = this.prefs.getIntPref("leftClick");
this.disablePauseResume = this.prefs.getBoolPref("disablePauseResume");
this.tooltip = this.prefs.getIntPref("tooltip");
this.clockType = this.prefs.getIntPref("clockType");
this.showSecs = this.prefs.getBoolPref("showSecs");
this.clock24Hr = this.prefs.getBoolPref("clock24Hr");
this.dateFormat = this.prefs.getIntPref("dateFormat");
this.countdownHours = this.prefs.getIntPref("countdownHours");
this.countdownMinutes = this.prefs.getIntPref("countdownMinutes");
this.countdownSeconds = this.prefs.getIntPref("countdownSeconds");
// May contain Unicode characters.
this.countdowns = this.prefs.getComplexValue("countdowns",
Components.interfaces.nsISupportsString).data;
this.favCountdowns = this.prefs.getComplexValue("favCountdowns",
Components.interfaces.nsISupportsString).data;
this.notifyRecur = this.prefs.getComplexValue("notifyRecur",
Components.interfaces.nsISupportsString).data;
// As of ver 1.7, userWavPath a misnomer.
if ( this.prefs.prefHasUserValue("userWavPath") ) {
this.customAudioFile = this.prefs.getComplexValue("userWavPath",
Components.interfaces.nsILocalFile);
try {
if ( this.customAudioFile.isFile() ) {
this.customAudioPath = this.customAudioFile.path;
if ( this.audioObjectSupport &&
this.customAudioPath.substr(this.customAudioPath.length - 4) === ".ogg") {
this.usingAudioObject = true;
this.audioObject.src = "file:///" + this.customAudioPath;
// Seems to fail silently without proper file header.
this.audioObject.load();
}
else {
this.usingAudioObject = false;
}
}
}
catch(e) {
alert(strbundle.getString("alert.error.invalid.audio.file") +
"\n" + e.name);
}
}
else {
this.usingAudioObject = false;
}
if ( this.prefs.prefHasUserValue("eventLogPath") ) {
this.eventLogPath = this.prefs.getComplexValue("eventLogPath",
Components.interfaces.nsILocalFile).path;
}
}
catch(e) {
alert(strbundle.getString("alert.error.get.prefs") +
"\n" + e.message);
}
// Time the browser session.
this.startBrowserTime = new Date();
// Create default logging file.
if ( !this.eventLogPath ) {
SimpleTimerEventLog.createDefaultLogFile();
}
// Build a notifications table.
if ( this.notifyRecur ) {
SimpleTimerNotify.buildNotifyTableFromPref(this.notifyRecur);
this.getNotifyTable();
}
// Is toolbar button in palette or on bar?
// This is checked again whenever the "Customize Toolbar" window is closed.
var toolbarItem = document.getElementById("simtim-toolbarItem");
this.buttonOnToolbar = ( toolbarItem ) ? true : false;
// Check which bar we are using.
if ( this.displayIn === 0 ) {
// Statusbar.
this.setStatusbar();
this.setIconImage(this.barImageElement);
}
else if ( this.displayIn === 1 ) {
// Toolbar.
if ( this.buttonOnToolbar ) {
this.setToolbar();
this.setIconImage(this.barImageElement);
}
else {
// Warn user to move button from palette to toolbar.
document.getElementById("simtim-statpanelTimer").hidden = true;
var params = { displayItems: null, msg: strbundle.getString("alert.warning.toolbar.button") };
SimpleTimerSliderAlert.addMessageToQueue(params);
}
}
else {
// Both.
this.setStatusbar();
if ( this.buttonOnToolbar ) {
this.setToolbar();
document.getElementById("simtim-toolbarItem").hidden = false;
document.getElementById("simtim-statpanelTimer").hidden = false;
}
else {
var params = { displayItems: null, msg: strbundle.getString("alert.warning.toolbar.button") };
SimpleTimerSliderAlert.addMessageToQueue(params);
}
this.setIconImage("both");
}
// If there are notifications, start timing.
if ( this.notifyRecur ) {
this.setNotifyInterval();
}
// Build a countdowns table.
if ( this.countdowns ) {
// Incomplete timers from previous session.
SimpleTimerCountdown.buildCountdownTableFromPref(this.countdowns);
this.getCountdownTable();
if ( this.countdownTable.length > 0 ) {
// Continue timers. Supercedes auto functions.
this.setCountdownTimer();
return;
}
else {
// Timers from previous session have all expired. Update countdowns pref.
this.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
}
}
// Check if countup in progress in other browser windows.
if ( this.countupInOtherBrowserWindow() ) {
// Do likewise. Supercedes auto functions.
return;
}
// Check for auto funtions.
switch ( this.onStartup ) {
case 0:
break;
case 1:
this.clock();
break;
case 2:
this.countUp();
break;
default:
alert(strbundle.getString("alert.error.invalid.startup"));
}
},
// Clean up after ourselves.
shutdown: function() {
this.prefs.removeObserver("", this);
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "domwindowopened");
if ( this.audioObjectSupport ) {
this.audioObject.removeEventListener("loadedmetadata", function(event) { SimpleTimer.audioPlaybackEnded(event); }, false);
this.audioObject.removeEventListener("ended", function(event) { SimpleTimer.audioPlaybackEnded(event); }, false);
this.audioObject.removeEventListener("pause", function(event) { SimpleTimer.audioPlaybackPause(event); }, false);
this.audioObject.removeEventListener("abort", function(event) { SimpleTimer.audioPlaybackAbort(event); }, false);
this.audioObject.removeEventListener("error", function(event) { SimpleTimer.audioPlaybackError(event); }, false);
}
},
// Called when events occur on the preferences, or a window is opened/closed..
observe: function(subject, topic, data) {
var strbundle = document.getElementById("simtim-strings");
if ( topic === "domwindowopened" ) {
// Window hasn't loaded yet, so we don't know which window this is.
// Only interested in "Customize Toolbar" window, so add a load listener.
subject.addEventListener("load", function(event) { SimpleTimer.windowLoaded(event); }, false);
}
else if ( topic === "domwindowclosed" ) {
// Only called when "Customize Toolbar" window is closed.
var onToolbarWhenOpened = this.buttonOnToolbar;
this.buttonOnToolbar = ( document.getElementById("simtim-toolbarItem") ) ?
true : false;
var onToolbarWhenClosed = this.buttonOnToolbar;
if ( !onToolbarWhenOpened && onToolbarWhenClosed ) {
// User just dragged button to toolbar...
if ( this.displayIn > 0 ) {
// ...and it's in play.
this.setToolbar();
if ( this.displayIn === 2 ) {
document.getElementById("simtim-statpanelTimer").hidden = false;
}
if ( this.clockInProgress || this.countdownInProgress || this.countupInProgress ) {
this.changeBarMode("wake");
}
else {
this.changeBarMode("sleep");
}
}
else {
// ...but display is set to statusbar.
// Warn user to change display option.
alert(strbundle.getString("alert.warning.toolbar.option"));
}
}
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "domwindowclosed");
}
else if ( topic === "nsPref:changed" ) {
switch ( data ) {
case "popupNotify":
this.popupNotify = this.prefs.getBoolPref("popupNotify");
break;
case "audioNotify":
this.audioNotify = this.prefs.getBoolPref("audioNotify");
break;
case "dialogNotify":
this.dialogNotify = this.prefs.getBoolPref("dialogNotify");
break;
case "repeatAudio":
this.repeatAudio = this.prefs.getIntPref("repeatAudio");
break;
case "repeatMax":
this.repeatMax = this.prefs.getIntPref("repeatMax");
break;
case "audioFile":
this.audioFile = this.prefs.getIntPref("audioFile");
break;
case "userWavPath":
this.customAudioFile = this.prefs.getComplexValue("userWavPath",
Components.interfaces.nsILocalFile);
try {
if ( this.customAudioFile.isFile() ) {
this.customAudioPath = this.customAudioFile.path;
if ( this.audioObjectSupport && this.customAudioPath.substr(this.customAudioPath.length - 4) === ".ogg") {
this.usingAudioObject = true;
this.audioObject.src = "file:///" + this.customAudioPath;
this.audioObject.load();
}
else {
this.usingAudioObject = false;
}
}
}
catch(e) {
alert(strbundle.getString("alert.error.invalid.audio.file") +
"\n" + e.name);
}
break;
case "displayIn":
this.displayIn = this.prefs.getIntPref("displayIn");
if ( this.displayIn === 0 ) {
// Statusbar.
this.setStatusbar();
}
else if ( this.displayIn === 1 ) {
// Toolbar.
this.setToolbar();
}
else {
// Both.
if ( document.getElementById("simtim-statpanelTimer").hidden ) {
// Switching from toolbar display.
this.setStatusbar();
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarItem").hidden = false;
}
}
else {
// Switching from statusbar display.
this.setToolbar();
document.getElementById("simtim-statpanelTimer").hidden = false;
}
}
if ( this.clockInProgress || this.countdownInProgress || this.countupInProgress ) {
this.changeBarMode("wake");
}
else {
this.changeBarMode("sleep");
}
break;
case "onStartup":
this.onStartup = this.prefs.getIntPref("onStartup");
break;
case "eventLogging":
this.eventLogging = this.prefs.getBoolPref("eventLogging");
break;
case "eventLogPath":
this.eventLogPath = this.prefs.getComplexValue("eventLogPath",
Components.interfaces.nsILocalFile).path;
break;
case "deleteLogEntries":
this.deleteLogEntries = this.prefs.getIntPref("deleteLogEntries");
break;
case "leftClick":
this.leftClick = this.prefs.getIntPref("leftClick");
break;
case "disablePauseResume":
this.disablePauseResume = this.prefs.getBoolPref("disablePauseResume");
break;
case "tooltip":
this.tooltip = this.prefs.getIntPref("tooltip");
break;
case "showSecs":
this.showSecs = this.prefs.getBoolPref("showSecs");
break;
case "clockType":
this.clockType = this.prefs.getIntPref("clockType");
break;
case "clock24Hr":
this.clock24Hr = this.prefs.getBoolPref("clock24Hr");
break;
case "dateFormat":
this.dateFormat = this.prefs.getIntPref("dateFormat");
break;
case "countdownHours":
this.countdownHours = this.prefs.getIntPref("countdownHours");
break;
case "countdownMinutes":
this.countdownMinutes = this.prefs.getIntPref("countdownMinutes");
break;
case "countdownSeconds":
this.countdownSeconds = this.prefs.getIntPref("countdownSeconds");
break;
case "notifyClosed":
// User clicked OK.
if ( this.getNotifyTable() ) {
this.clearNotifyInterval();
this.setNotifyInterval();
}
break;
case "countdowns":
this.countdowns = this.prefs.getComplexValue("countdowns",
Components.interfaces.nsISupportsString).data;
break;
case "favCountdowns":
this.favCountdowns = this.prefs.getComplexValue("favCountdowns",
Components.interfaces.nsISupportsString).data;
break;
case "notifyRecur":
this.notifyRecur = this.prefs.getComplexValue("notifyRecur",
Components.interfaces.nsISupportsString).data;
break;
case "notifyTime":
// Kept for backward compatibility v0.5 and earlier.
break;
case "insertBefore":
break;
default:
alert(strbundle.getString("alert.error.unknown.pref"));
}
}
},
// Called when a window is loaded.
windowLoaded: function(evt) {
var doc = evt.originalTarget;
var win = doc.defaultView;
win.removeEventListener("load", function(event) { SimpleTimer.windowLoaded(event); }, false);
if ( doc.documentElement.getAttribute("id") === "CustomizeToolbarWindow" ) {
// Observe when Customize Toolbar window is closed to see if user
// dragged button to toolbar.
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "domwindowclosed", false);
}
},
// Using statusbar.
setStatusbar: function() {
this.barContainerElement = "simtim-statpanelTimer";
this.barImageElement = "simtim-statpanelImage";
this.barLabelElement = "simtim-statpanelLabel";
// Make sure the button is not in the palette.
if ( this.buttonOnToolbar ) {
var status = document.getElementById("simtim-toolbarButton").
getAttribute("status");
document.getElementById("simtim-statpanelImage").
setAttribute("status", status);
document.getElementById("simtim-statpanelLabel").value =
document.getElementById("simtim-toolbarLabel").value;
document.getElementById("simtim-toolbarItem").hidden = true;
}
document.getElementById("simtim-statpanelTimer").hidden = false;
},
// Using toolbar.
setToolbar: function() {
var strbundle = document.getElementById("simtim-strings");
this.barContainerElement = "simtim-toolbarItem";
this.barImageElement = "simtim-toolbarButton";
this.barLabelElement = "simtim-toolbarLabel";
if ( this.buttonOnToolbar ) {
var status = document.getElementById("simtim-statpanelImage").
getAttribute("status");
document.getElementById("simtim-toolbarButton").
setAttribute("status", status);
document.getElementById("simtim-toolbarLabel").value =
document.getElementById("simtim-statpanelLabel").value;
document.getElementById("simtim-toolbarItem").hidden = false;
}
else {
// Warn user to move button from palette to toolbar.
alert(strbundle.getString("alert.warning.toolbar.button"));
}
document.getElementById("simtim-statpanelTimer").hidden = true;
},
// Displays a time value on a bar.
displayTime: function(hours, mins, secs) {
var displayTime = ( hours > 0 ) ?
hours + ":" + this.padTime(mins) + ":" + this.padTime(secs) :
this.padTime(mins) + ":" + this.padTime(secs);
if ( this.displayIn === 2 ) {
// Both.
document.getElementById("simtim-statpanelLabel").value = displayTime;
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarLabel").value = displayTime;
}
}
else {
var barLabelElement = document.getElementById(this.barLabelElement);
if ( barLabelElement ) {
barLabelElement.value = displayTime;
}
}
},
// For tooltips that include countdown timers, refresh tooltip every second.
setTooltip: function() {
this.fillTooltip();
if ( (( this.tooltip === 4 && this.countdownTable.length > 0 ) ||
this.tooltip === 2 ) &&
this.timerTooltipID === 0 ) {
this.timerTooltipID = setInterval("SimpleTimer.fillTooltip()", 1000);
}
},
// Called on statpanel or toolbar button mouseout.
clearTooltip: function() {
clearInterval(this.timerTooltipID);
this.timerTooltipID = 0;
},
// Fill the bar tooltip based on user pref.
fillTooltip: function() {
var strbundle = document.getElementById("simtim-strings");
switch ( this.tooltip ) {
case 0:
// No tooltip.
this.completeTooltip("remove", "");
break;
case 1:
// Date.
document.getElementById("simtim-descTtipCurrDate0").value =
new Date().toLocaleDateString();
this.completeTooltip("set", "simtim-ttipDate");
break;
case 2:
// Full tooltip.
// Date.
document.getElementById("simtim-descTtipCurrDate1").value =
new Date().toLocaleDateString();
// Notifications.
var parent = document.getElementById("simtim-gridNotify");
var oldRowsNode = parent.lastChild;
var newRowsNode = document.createElement("rows");
var descrHeader = document.createElement("description");
var rowNode, descrNodeTime, descrNodeDescription;
var msg;
if ( this.notifyID ) {
// Give rows element an id for styling.
newRowsNode.setAttribute("id", "simtim-rowsNotify");
// Display a header line.
descrHeader.setAttribute("value",
strbundle.getString("msg.notifications"));
newRowsNode.appendChild(descrHeader);
for ( var i in this.notifyTable ) {
// Bypass completed nots.
this.notification = this.notifyTable[i];
if ( this.notification.completed ) {
continue;
}
rowNode = document.createElement("row");
descrNodeTime = document.createElement("description");
descrNodeTime.setAttribute("value", this.notification.timeDisplay);
rowNode.appendChild(descrNodeTime);
msg = " ";
msg += this.notification.description ?
'"' + this.notification.description + '"' :
strbundle.getString("msg.no.description");
msg += this.notification.url ?
" " + "[URL]" : "";
msg += this.notification.day >= 0 ?
" / " + SimpleTimerNotify.getDow(this.notification.day) : "";
descrNodeDescription = document.createElement("description");
descrNodeDescription.setAttribute("value", msg);
rowNode.appendChild(descrNodeDescription);
newRowsNode.appendChild(rowNode);
}
}
else {
// No notifications.
rowNode = document.createElement("row");
descrHeader.setAttribute("value",
strbundle.getString("msg.no.notifications"));
rowNode.appendChild(descrHeader);
newRowsNode.appendChild(rowNode);
}
parent.replaceChild(newRowsNode, oldRowsNode);
// Countdown timers.
parent = document.getElementById("simtim-gridCountdown");
this.createTimerTooltip(parent);
// Count up.
msg = this.savedCountupTime ?
strbundle.getString("msg.last.countup") +
" " + this.savedCountupTime :
strbundle.getString("msg.no.countup");
document.getElementById("simtim-descTtipLastCountUp").value = msg;
// Browser time.
document.getElementById("simtim-descTtipSessionTime").value =
strbundle.getString("msg.browser.time") +
" " + this.calcBrowserTime();
// Set tooltip.
this.completeTooltip("set", "simtim-ttipFull");
break;
case 3:
// Date, but only if clock running.
if ( this.clockInProgress ) {
document.getElementById("simtim-descTtipCurrDate0").value =
new Date().toLocaleDateString();
this.completeTooltip("set", "simtim-ttipDate");
}
else {
this.completeTooltip("remove", "");
}
break;
case 4:
// Countdown timers.
parent = document.getElementById("simtim-gridTtipCountdown");
this.createTimerTooltip(parent);
// Set tooltip.
this.completeTooltip("set", "simtim-ttipCountdown");
break;
default:
this.debug(strbundle.getString("alert.error.invalid.tooltip"));
}
},
// Set or remove tooltip.
completeTooltip: function(action, value) {
var barContainerElement = document.getElementById(this.barContainerElement);
if ( this.displayIn === 2 ) {
// Both.
if ( action === "set") {
document.getElementById("simtim-statpanelTimer").setAttribute("tooltip", value);
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarItem").setAttribute("tooltip", value);
}
}
else if ( document.getElementById("simtim-statpanelTimer").hasAttribute("tooltip") ) {
document.getElementById("simtim-statpanelTimer").removeAttribute("tooltip");
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarItem").removeAttribute("tooltip");
}
}
}
else {
if ( action === "set") {
barContainerElement && barContainerElement.setAttribute("tooltip", value);
}
else if ( barContainerElement &&
barContainerElement.hasAttribute("tooltip") ) {
barContainerElement.removeAttribute("tooltip");
}
}
},
// Countdown timer portion of bar tooltip.
createTimerTooltip: function(parent) {
// Countdown timers. Create description elements, add them
// as children to a row element (in a rows elememt in a grid),
// then replace old rows with new one.
var strbundle = document.getElementById("simtim-strings");
var oldRowsNode = parent.lastChild;
var newRowsNode = document.createElement("rows");
var descrHeader = document.createElement("description");
var rowNode, descrNodeTime, descrNodeDescription;
var msg, displayTime, countdownTimeMilli, countdownTime,
displayHours, displayMinutes, displaySeconds;
if ( this.countdownTable.length === 0 ) {
rowNode = document.createElement("row");
descrHeader.setAttribute("value",
strbundle.getString("msg.no.timers"));
rowNode.appendChild(descrHeader);
newRowsNode.appendChild(rowNode);
}
else {
if ( this.tooltip === 2 ) {
// Give rows element an id for styling.
newRowsNode.setAttribute("id", "simtim-rowsTimers");
}
// Display a header line.
descrHeader.setAttribute("value",
strbundle.getString("msg.countdown.timers"));
newRowsNode.appendChild(descrHeader);
var currDate = new Date();
var currTimeMilli = currDate.getTime() - currDate.getMilliseconds();
for ( var j in this.countdownTable ) {
rowNode = document.createElement("row");
descrNodeTime = document.createElement("description");
if ( j === "0" && this.paused ) {
if ( this.displayIn === 2 ) {
displayTime = document.getElementById("simtim-statpanelLabel").value;
}
else {
var barLabelElement = document.getElementById(this.barLabelElement);
if ( barLabelElement ) {
displayTime = barLabelElement.value;
}
}
descrNodeTime.setAttribute("value", displayTime);
}
else {
countdownTimeMilli = this.countdownTable[j].timeFinishedMilli - currTimeMilli;
countdownTime = new Date(countdownTimeMilli);
displayHours = countdownTime.getUTCHours();
displayMinutes = this.padTime(countdownTime.getUTCMinutes());
displaySeconds = this.padTime(countdownTime.getUTCSeconds());
if ( countdownTimeMilli > 0 ) {
displayTime = ( displayHours > 0 ) ?
displayHours + ":" + displayMinutes + ":" + displaySeconds :
displayMinutes + ":" + displaySeconds;
descrNodeTime.setAttribute("value", displayTime);
}
else {
descrNodeTime.setAttribute("value", "00:00");
}
}
rowNode.appendChild(descrNodeTime);
descrNodeDescription = document.createElement("description");
msg = this.countdownTable[j].description ?
'"' + this.countdownTable[j].description + '"' :
strbundle.getString("msg.no.description");
msg += this.countdownTable[j].recurring ?
" / " + strbundle.getString("msg.recurring") : "";
descrNodeDescription.setAttribute("value", msg);
rowNode.appendChild(descrNodeDescription);
newRowsNode.appendChild(rowNode);
}
}
parent.replaceChild(newRowsNode, oldRowsNode);
},
// Check if there is a timer already in progress.
// If last action was count up, save time for tooltip.
cancelTimerInProgress: function() {
var strbundle = document.getElementById("simtim-strings");
if ( this.timerID ) {
window.clearInterval(window.SimpleTimer.timerID);
this.timerID = 0;
this.startTime = null;
if ( this.displayIn === 2 ) {
// Both.
document.getElementById("simtim-statpanelLabel").value = "";
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarLabel").value = "";
}
}
else {
var barLabelElement = document.getElementById(this.barLabelElement);
if ( barLabelElement ) {
barLabelElement.value = "";
}
}
}
if ( this.countupInProgress ) {
this.countupInProgress = false;
this.savedCountupTime = this.padTime(this.elapsedHours) + ":" +
this.padTime(this.elapsedMins) + ":" +
this.padTime(this.elapsedSecs);
if ( this.eventLogging ) {
// Pass event type, event time, recurring, status, description(N/A), URL(N/A).
SimpleTimerEventLog.logEvent(strbundle.getString("msg.countup"),
this.savedCountupTime,
strbundle.getString("msg.not.applicable"),
strbundle.getString("msg.completed"),
strbundle.getString("msg.not.applicable"),
strbundle.getString("msg.not.applicable"));
}
}
this.countdownInProgress = false;
this.clockInProgress = false;
this.paused = false;
},
// Change the bar display between icon and label.
changeBarMode: function(mode) {
if ( mode === "wake" ) {
// Wake up panel if sleeping.
if ( this.displayIn === 2 ) {
// Both.
if ( document.getElementById("simtim-statpanelLabel").collapsed ) {
document.getElementById("simtim-statpanelLabel").value = "";
document.getElementById("simtim-statpanelLabel").collapsed = false;
document.getElementById("simtim-statpanelImage").collapsed = true;
}
if ( this.buttonOnToolbar &&
document.getElementById("simtim-toolbarLabel").collapsed ) {
document.getElementById("simtim-toolbarLabel").value = "";
document.getElementById("simtim-toolbarLabel").collapsed = false;
document.getElementById("simtim-toolbarButton").collapsed = true;
}
}
else if ( document.getElementById(this.barContainerElement) &&
document.getElementById(this.barLabelElement).collapsed ) {
document.getElementById(this.barLabelElement).value = "";
document.getElementById(this.barLabelElement).collapsed = false;
document.getElementById(this.barImageElement).collapsed = true;
}
}
else {
// Sleep panel if awake.
if ( this.displayIn === 2 ) {
// Both.
if ( document.getElementById("simtim-statpanelImage").collapsed ) {
document.getElementById("simtim-statpanelImage").collapsed = false;
document.getElementById("simtim-statpanelLabel").collapsed = true;
}
if ( this.buttonOnToolbar &&
document.getElementById("simtim-toolbarButton").collapsed ) {
document.getElementById("simtim-toolbarButton").collapsed = false;
document.getElementById("simtim-toolbarLabel").collapsed = true;
}
}
else if ( document.getElementById(this.barContainerElement) &&
document.getElementById(this.barImageElement).collapsed ) {
document.getElementById(this.barImageElement).collapsed = false;
document.getElementById(this.barLabelElement).collapsed = true;
}
}
},
// Calculate elapsed time this browser session, displayed in tooltip.
calcBrowserTime: function() {
// Round elapsed milli to nearest second
var browseMilli = new Date().getTime() - this.startBrowserTime.getTime();
browseMilli = Math.round(browseMilli / 1000) * 1000;
var browseSecs = ( browseMilli / 1000 ) % 60;
var browseMins = (Math.floor(browseMilli / ( 1000 * 60 ))) % 60;
var browseHours = Math.floor(browseMilli / ( 1000 * 60 * 60 ));
return ( this.padTime(browseHours) + ":" +
this.padTime(browseMins) + ":" +
this.padTime(browseSecs) );
},
// Calculate elapsed time. Used by both countup and countdown.
calcElapsedTime: function() {
// Round elapsed milli to nearest second.
this.elapsedMilli = new Date().getTime() - this.startTime.getTime();
this.elapsedMilli = Math.round(this.elapsedMilli / 1000) * 1000;
this.elapsedSecs = ( this.elapsedMilli / 1000 ) % 60;
this.elapsedMins = (Math.floor(this.elapsedMilli / ( 1000 * 60 ))) % 60;
this.elapsedHours = Math.floor(this.elapsedMilli / ( 1000 * 60 * 60 ));
},
// Calculate remaining time for countdown.
calcRemainingTime: function() {
this.remainingMilli = this.countdownMilli - this.elapsedMilli;
this.remainingSecs = ( this.remainingMilli / 1000 ) % 60;
this.remainingMins = (Math.floor(this.remainingMilli / ( 1000 * 60 ))) % 60;
this.remainingHours = Math.floor(this.remainingMilli / ( 1000 * 60 * 60 ));
},
// Pad a display time with zero if necessary.
padTime: function(time) {
return ( ( time < 10 ) ? "0" + time : time );
},
// Convert a clock (hh:mm:ss) to milliseconds.
convertClockToMilli: function(clock) {
return ( ( parseInt(clock, 10) * 60 * 60 * 1000 ) +
( parseInt(clock.substr(clock.indexOf(":") + 1), 10) * 60 * 1000 ) +
( parseInt(clock.substr(clock.lastIndexOf(":") + 1), 10) * 1000 ) );
},
// Left-click on icon is user-defined. Left-click during count up
// or count down is pause/resume (can be disabled in Options). Middle-click is stop/reset.
onPanelClick: function(event) {
if ( event.button === 0 ) {
// Process left mouse clicks.
if ( this.countdownInProgress || this.countupInProgress ) {
if ( !this.disablePauseResume ) {
this.pauseResume();
}
}
else if ( this.clockInProgress ) {
// something someday.
}
else {
// Icon must be displayed.
switch ( this.leftClick ) {
case 0:
break;
case 1:
this.clock();
break;
case 2:
this.countUp();
break;
case 3:
this.countDown();
break;
case 4:
this.notifyMe();
break;
default:
var strbundle = document.getElementById("simtim-strings");
alert(strbundle.getString("alert.error.invalid.leftClick"));
}
}
}
else if ( event.button === 1 &&
( this.timerID || this.paused )) {
// Middle-click. Paused countup has no timer id.
this.stop();
}
},
// Called when user selects Clock menuitem, or left-click
// bar icon if defined.
clock: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
win.SimpleTimer.cancelTimerInProgress();
if ( win.SimpleTimer.countdowns ) {
win.SimpleTimer.countdownTable = [];
win.SimpleTimer.setCountdownTable();
win.SimpleTimerCountdown.updateCountdownPref();
}
win.SimpleTimer.changeBarMode("wake");
win.SimpleTimer.startTime = new Date();
win.SimpleTimer.clockInProgress = true;
win.SimpleTimer.timerID = win.setInterval(win.SimpleTimer.displayClock, 1000, "clock");
}
},
// Display a clock on statusbar or toolbar using current time (mode = "clock").
// Return time to Timer Completed dialog (mode = "dialog").
displayClock: function(mode) {
var displayTime = "";
var date = new Date();
if ( SimpleTimer.clockType === 1 ) {
// Display Epoch time, seconds since Jan 1, 1970 GMT 0:00.
displayTime = ( date.getTime() - date.getMilliseconds() ) / 1000;
}
else if ( SimpleTimer.clock24Hr ) {
// User preference is to use 24 hr clock.
if ( SimpleTimer.showSecs ) {
// User preference is to show seconds.
displayTime = SimpleTimer.padTime(date.getHours()) +
":" + SimpleTimer.padTime(date.getMinutes()) +
":" + SimpleTimer.padTime(date.getSeconds());
}
else {
displayTime = SimpleTimer.padTime(date.getHours()) +
":" + SimpleTimer.padTime(date.getMinutes());
}
}
else {
if ( SimpleTimer.showSecs ) {
displayTime = date.toLocaleTimeString();
}
else {
var time = date.toLocaleTimeString().split(" ");
if ( time.length === 1 || time[0].indexOf(":") >= 0 ) {
// Local time is 24 hr clock, or 12 hr clock where
// time value precedes AM/PM chars (typical).
// Discard the seconds.
displayTime = time[0].substr(0, time[0].lastIndexOf(":"));
// If local time is 12 hr, time[1] will be AM/PM chars.
displayTime = ( time.length > 1 ) ?
displayTime + " " + time[1] : displayTime;
}
else {
// AM/PM chars. precede time value eg. Korean
displayTime = time[0] + " " +
time[1].substr(0, time[1].lastIndexOf(":"));
}
}
}
if ( mode === "clock") {
// Append a date as indicated by user in Options.
displayTime += SimpleTimer.getFormattedDate(date);
if ( SimpleTimer.displayIn === 2 ) {
// Both.
document.getElementById("simtim-statpanelLabel").value = displayTime;
if ( SimpleTimer.buttonOnToolbar ) {
document.getElementById("simtim-toolbarLabel").value = displayTime;
}
}
else {
var barLabelElement = document.getElementById(SimpleTimer.barLabelElement);
if ( barLabelElement ) {
barLabelElement.value = displayTime;
}
}
}
// Return value used in Timer Completed load routine.
// Ignored in clock mode.
return displayTime;
},
// Return a formatted date as indicated by user in Options.
getFormattedDate: function(date) {
switch ( this.dateFormat ) {
case 0:
// No date.
return "";
case 1:
// eg. Fri.
return date.toLocaleFormat(" %a");
case 2:
// eg. Fri May 29.
return date.toLocaleFormat(" %a %b %d");
case 3:
// eg. Fri May 29, 2009.
return date.toLocaleFormat(" %a %b %d, %Y");
case 4:
// eg. Fri 05/29/09.
return date.toLocaleFormat(" %a %m/%d/%y");
case 5:
// eg. Fri 05/29/2009.
return date.toLocaleFormat(" %a %m/%d/%Y");
case 6:
// eg. Fri 29/05/09.
return date.toLocaleFormat(" %a %d/%m/%y");
case 7:
// eg. Fri 29/05/2009.
return date.toLocaleFormat(" %a %d/%m/%Y");
case 8:
// eg. Fri 29.
return date.toLocaleFormat(" %a %d");
case 9:
// eg. Fri 29th.
var numDate = date.getDate();
return date.toLocaleFormat(" %a") + " " +
numDate + this.getOrdinalChars(numDate);
default:
var strbundle = document.getElementById("simtim-strings");
alert(strbundle.getString("alert.error.invalid.dateFormat"));
return "";
}
},
// Return chars representing ordinal.
getOrdinalChars: function(num) {
var strbundle = document.getElementById("simtim-strings");
return ( num % 10 === 1 && num % 100 !== 11 ) ? strbundle.getString("display.ordinal.st") :
( num % 10 === 2 && num % 100 !== 12 ) ? strbundle.getString("display.ordinal.nd") :
( num % 10 === 3 && num % 100 !== 13 ) ? strbundle.getString("display.ordinal.rd") :
strbundle.getString("display.ordinal.th");
},
// Called when user selects Notify Me At menuitem.
notifyMe: function() {
this.setNotifyTable();
// Display Notify Me At dialog, non-modal.
window.openDialog(
"chrome://simpletimer/content/simpletimer-notify.xul",
"",
"centerscreen, chrome, resizable, dependent");
},
// Called at startup and after Notify Me At dialog closed (via OK).
getNotifyTable: function() {
var strbundle = document.getElementById("simtim-strings");
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
this.notifyTable = Application.storage.get("notifyTable", "ERR");
if ( this.notifyTable === "ERR" ) {
alert(strbundle.getString("alert.error.loading.notifications"));
return false;
}
else {
return true;
}
},
// Called before Notify Me At dialog opened.
setNotifyTable: function() {
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
Application.storage.set("notifyTable", this.notifyTable);
},
// Set icon image based on pending nots.
setIconImage: function(elem) {
// Check if any pending nots., and set icon image
var status = "normal";
var len = this.notifyTable.length;
for ( var i = 0; i < len; i++ ) {
if ( this.notifyTable[i].completed === false ) {
status = "notify";
break;
}
}
if ( elem === "both" ) {
document.getElementById("simtim-statpanelImage").
setAttribute("status", status);
if ( this.buttonOnToolbar ) {
document.getElementById("simtim-toolbarButton").
setAttribute("status", status);
}
}
else {
document.getElementById(this.barImageElement) &&
document.getElementById(this.barImageElement).
setAttribute("status", status);
}
},
// Clear notify interval.
clearNotifyInterval: function() {
this.setIconImage();
clearInterval(this.notifyID);
this.notifyID = 0;
},
// Set notify interval based on soonest pending notify.
setNotifyInterval: function() {
if ( !this.notifyTable || this.notifyTable.length === 0 ) {
return;
}
var intervalTime = 0;
// For the not. selected to time, this is the index into the not. table.
this.currNotifyIndex = 0;
// For the not. selected to time, this is the time until completion.
this.currNotifyInterval = 0;
var currDate = new Date();
var currTimeMilli = currDate.getTime() - ( currDate.getSeconds() * 1000 );
// Find the not. with the smallest interval time.
for ( var i in this.notifyTable ) {
// Bypass completed nots.
if ( this.notifyTable[i].completed === true ) {
continue;
}
intervalTime = this.notifyTable[i].timeMilli - currTimeMilli;
if ( this.currNotifyInterval === 0 ||
intervalTime < this.currNotifyInterval ) {
this.currNotifyInterval = intervalTime;
this.currNotifyIndex = i;
}
}
// No point checking for completion every second if we are more than
// a minute from completion.
if ( this.currNotifyInterval ) {
this.notifyID = ( this.currNotifyInterval > ( 60 * 1000 ) ) ?
setTimeout("SimpleTimer.switchNotifyInterval()",
(this.currNotifyInterval - ( 60 * 1000 ))) :
setInterval("SimpleTimer.monitorLastMinuteNotify()", 1000);
}
},
// Change intervals when its one minute prior to notify. Called once.
switchNotifyInterval: function() {
clearTimeout(this.notifyID);
this.notifyID = setInterval("SimpleTimer.monitorLastMinuteNotify()", 1000);
},
// Is it time to notify the user?
monitorLastMinuteNotify: function() {
var currDate = new Date();
if ( currDate.getTime() > this.notifyTable[this.currNotifyIndex].timeMilli ) {
// We're done.
var timeoutIncrement = 0;
this.completedIndices = [];
this.clearNotifyInterval();
// Check if multiple nots. expired at same time.
// Add a 3 second delay between the alerts.
for ( var i in this.notifyTable ) {
if ( this.notifyTable[i].completed === false &&
currDate.getTime() > this.notifyTable[i].timeMilli ) {
this.completedIndices.push(i);
setTimeout("SimpleTimer.processCompletedNotify()", timeoutIncrement);
setTimeout("SimpleTimer.setIconImage()", timeoutIncrement + 1000);
if ( !this.notifyTable[i].recurring || this.notifyToday(i) ) {
timeoutIncrement += 3000;
}
}
}
timeoutIncrement += 1000;
setTimeout("SimpleTimer.setNotifyInterval()", timeoutIncrement);
}
},
// A completed notification is not deleted from table,
// but updated.
processCompletedNotify: function() {
var strbundle = document.getElementById("simtim-strings");
if ( this.completedIndices.length > 0 ) {
// Multiple nots. expired at the same time.
// Grab (and remove) the first element (not. table index).
this.currNotifyIndex = this.completedIndices.shift();
}
var recurring = this.notifyTable[this.currNotifyIndex].recurring;
if ( !recurring || this.notifyToday(this.currNotifyIndex) ) {
this.checkUserPrefs("notify");
// Link if URL provided but dialog alarm not used.
if ( this.notifyTable[this.currNotifyIndex].url &&
!this.dialogNotify &&
( this.audioNotify ||
this.popupNotify ) ) {
this.autoOpenLink(this.notifyTable[this.currNotifyIndex].url);
}
if ( this.eventLogging ) {
var eventTime = this.notifyTable[this.currNotifyIndex].timeDisplay;
var eventRecurring = ( recurring ) ?
SimpleTimerNotify.getDow(this.notifyTable[this.currNotifyIndex].day) :
strbundle.getString("msg.no");
var eventDescription = ( this.notifyTable[this.currNotifyIndex].description ) ?
this.notifyTable[this.currNotifyIndex].description :
strbundle.getString("msg.none");
var eventUrl = ( this.notifyTable[this.currNotifyIndex].url ) ?
this.notifyTable[this.currNotifyIndex].url :
strbundle.getString("msg.none");
// Pass event type, event time, recurring, status, description, URL.
SimpleTimerEventLog.logEvent(strbundle.getString("msg.notification"),
eventTime,
eventRecurring,
strbundle.getString("msg.completed"),
eventDescription,
eventUrl);
}
}
if ( recurring ) {
// Recurring ones don't get marked completed.
// Add 1 day to not. milli time.
var notDate = new Date(this.notifyTable[this.currNotifyIndex].timeMilli);
notDate.setDate(notDate.getDate() + 1);
this.notifyTable[this.currNotifyIndex].timeMilli = notDate.getTime();
}
else {
// Mark this notification as completed.
this.notifyTable[this.currNotifyIndex].completed = true;
}
// Update notifications pref.
this.setNotifyTable();
SimpleTimerNotify.updateNotifyPref();
},
// Is today a notification day?
notifyToday: function(index) {
// Not. day values:
// 0-6 for Sun-Sat,
// 7 daily, 8 weekdays, 9 weekends.
var currDay = new Date().getDay();
var notDay = this.notifyTable[index].day;
if ( notDay === 7 || notDay === currDay ||
( notDay === 8 && ( currDay > 0 && currDay < 6 ) ) ||
( notDay === 9 && ( currDay === 0 || currDay === 6 ) ) ) {
return true;
}
else {
return false;
}
},
// Called when user selects Count Down menuitem.
countDown: function() {
this.setCountdownTable();
// Display time entry dialog, non-modal.
window.openDialog(
"chrome://simpletimer/content/simpletimer-countdown.xul",
"",
"centerscreen, chrome, resizable, dependent");
},
// Called after Countdown time entry dialog closed (via OK).
getCountdownTable: function() {
var strbundle = document.getElementById("simtim-strings");
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
var table = Application.storage.get("countdownTable", "ERR");
if ( table === "ERR" ) {
alert(strbundle.getString("alert.error.loading.countdowns"));
return false;
}
else {
// Independent copy of the array of countdown objects.
this.countdownTable = this.deepCopy(table);
return true;
}
},
// Called before Countdown time entry dialog opened.
setCountdownTable: function() {
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
var table = this.deepCopy(this.countdownTable);
Application.storage.set("countdownTable", table);
},
// Start countdown.
setCountdownTimer: function() {
if ( this.countdownTable.length === 0 ) {
SimpleTimer.cancelTimerInProgress();
SimpleTimer.changeBarMode("sleep");
// Update countdowns pref.
this.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
return;
}
this.startTime = new Date();
var currTimeMilli = this.startTime.getTime() - this.startTime.getMilliseconds();
for ( var i in this.countdownTable ) {
// For new timers set the completion time.
// Paused timer will stay on top (timeFinishedMilli = 0) until resumed.
if ( this.countdownTable[i].timeFinishedMilli === 0 &&
!this.countdownTable[i].paused ) {
this.countdownTable[i].timeFinishedMilli =
currTimeMilli + this.countdownTable[i].timeMilli;
}
}
// Sort the table. If the first entry is recurring, it may no longer be next to expire.
if ( this.countdownTable.length > 1 ) {
this.countdownTable.sort(SimpleTimerCountdown.sortCountdownTable);
}
// Update countdowns pref.
this.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
if ( this.countdownTable[0].paused ) {
// timeMilli was set to remainingMilli when paused.
this.paused = true;
this.countdownMilli = this.countdownTable[0].timeMilli;
}
else {
this.paused = false;
this.countdownMilli = this.countdownTable[0].timeFinishedMilli - currTimeMilli;
}
if ( this.countdownMilli < 0 ) {
// Sanity.
this.countdownMilli = 0;
}
var time = new Date(this.countdownMilli);
this.countdownHours = time.getUTCHours();
this.countdownMinutes = time.getUTCMinutes();
this.countdownSeconds = time.getUTCSeconds();
SimpleTimer.changeBarMode("wake");
this.displayTime(this.countdownHours, this.countdownMinutes,
this.countdownSeconds);
this.countdownInProgress = true;
// Perform decreaseTimer() every second.
this.timerID = setInterval("SimpleTimer.decreaseTimer()", 1000);
},
// Display the updated timer.
decreaseTimer: function() {
var strbundle = document.getElementById("simtim-strings");
if ( !this.paused ) {
this.calcElapsedTime();
}
if ( this.elapsedMilli < this.countdownMilli ) {
// Check if others (besides first timer/element) have expired.
// Possible if timer paused.
if ( this.countdownTable.length > 1 ) {
var eventDescription, eventRecurring;
var currDate = new Date();
var currTimeMilli = currDate.getTime() - currDate.getMilliseconds();
var len = this.countdownTable.length;
for ( var i = 1; i < len; i++ ) {
if ( currTimeMilli >= this.countdownTable[i].timeFinishedMilli ) {
// We're done.
this.checkUserPrefs(i); // Pass index.
if ( this.eventLogging ) {
eventRecurring = ( this.countdownTable[i].recurring ) ?
strbundle.getString("msg.yes") :
strbundle.getString("msg.no");
eventDescription = ( this.countdownTable[i].description ) ?
this.countdownTable[i].description :
strbundle.getString("msg.none");
// Pass event type, event time, recurring, status, description, URL(N/A).
SimpleTimerEventLog.logEvent(strbundle.getString("msg.countdown"),
this.countdownTable[i].timeDisplay,
eventRecurring,
strbundle.getString("msg.completed"),
eventDescription,
strbundle.getString("msg.not.applicable"));
}
if ( this.countdownTable[i].recurring ) {
// Cannot increment with timeMilli, since it changes if paused.
this.countdownTable[i].timeFinishedMilli +=
this.convertClockToMilli(this.countdownTable[i].timeDisplay);
}
else {
// Remove the completed timer.
this.countdownTable.splice(i, 1);
// Update countdowns pref.
this.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
}
}
}
}
if ( !this.paused ) {
// Update the statpanel display.
this.calcRemainingTime();
this.displayTime(this.remainingHours, this.remainingMins,
this.remainingSecs);
}
}
else {
// We're done.
this.cancelTimerInProgress();
this.displayTime(0, 0, 0);
this.checkUserPrefs("0"); // Pass index.
if ( this.eventLogging ) {
eventRecurring = ( this.countdownTable[0].recurring ) ?
strbundle.getString("msg.yes") :
strbundle.getString("msg.no");
eventDescription = ( this.countdownTable[0].description ) ?
this.countdownTable[0].description :
strbundle.getString("msg.none");
// Pass event type, event time, recurring, status, description, URL(N/A).
SimpleTimerEventLog.logEvent(strbundle.getString("msg.countdown"),
this.countdownTable[0].timeDisplay,
eventRecurring,
strbundle.getString("msg.completed"),
eventDescription,
strbundle.getString("msg.not.applicable"));
}
if ( this.countdownTable[0].recurring ) {
// Cannot increment with timeMilli, since it changes if paused.
this.countdownTable[0].timeFinishedMilli +=
this.convertClockToMilli(this.countdownTable[0].timeDisplay);
this.setCountdownTimer();
}
else {
if ( this.countdownTable.length < 2 ) {
this.countdownTable = [];
// Update countdowns pref.
this.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
if ( this.onStartup === 1 ) {
// Make sure all windows have completed countdown processing,
// as clock() cancels timer ids for all windows.
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
var allWinComplete = true;
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
if ( win.SimpleTimer.countdownTable.length !== 0 ) {
allWinComplete = false;
break;
}
}
if ( allWinComplete ) {
this.clock();
}
}
else {
this.changeBarMode("sleep");
}
}
else {
// Remove first element, and start the next one.
this.countdownTable.shift();
this.setCountdownTimer();
}
}
}
},
// Perform alerts if requested.
checkUserPrefs: function(mode) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
// Compare window references.
if ( win.window == window.window ) {
// Only perform the alerts once.
if ( this.audioNotify ) {
this.playSound(this.audioFile);
}
if ( this.popupNotify ) {
params = { displayItems: null, msg: this.getDisplayMsg(mode) };
SimpleTimerSliderAlert.addMessageToQueue(params);
}
if ( this.dialogNotify ) {
this.displayTimerCompleteDlg(mode);
}
}
},
// Play the audio file.
// This can be performed on countdown or notify completion,
// or if user clicks on audio icon in options dlg (possibly from Add-ons manager).
// Also performed when user requests audio repeated.
// Currently (1.7) not all .wav files supported by HTML5 audio, so use nsISound for them.
playSound: function(audioFile) {
var strbundle = document.getElementById("simtim-strings");
// Do this because may be via Add-ons manager.
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
if ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
}
var selectedAudioFile;
// Check which audio file user selected.
switch ( audioFile ) {
case 0:
selectedAudioFile = "alarm_clock";
break;
case 1:
selectedAudioFile = "fanfare";
break;
case 2:
selectedAudioFile = "custom";
break;
case 3:
selectedAudioFile = "notify";
break;
default:
selectedAudioFile = "alarm_clock";
}
if ( selectedAudioFile === "custom" && win.SimpleTimer.usingAudioObject ) {
// .ogg. Bypass if already playing.
// src set and loaded at startup, or when user sets file in Options.
if ( win.SimpleTimer.audioObject.ended ||
win.SimpleTimer.audioObject.paused ||
win.SimpleTimer.audioObject.error ) {
try {
// Reset in case paused.
win.SimpleTimer.audioObject.currentTime = 0.0;
win.SimpleTimer.audioObject.play();
win.SimpleTimer.debug("Simple Timer: ogg playback started");
// Take care of context menu for each browser window.
win.document.getElementById("simtim-mitemKillHtmlAudio").disabled = false;
while ( enumerator.hasMoreElements() ) {
win = enumerator.getNext();
win.document.getElementById("simtim-mitemKillHtmlAudio").disabled = false;
}
}
catch(e) {
alert(strbundle.getString("alert.error.audio.failed") +
"\n" + e.message);
}
}
}
else {
// .wav. The nsISound play method takes a URI as an argument,
// so first convert the URL to URI.
var soundURL = ( selectedAudioFile === "custom" ) ?
"file:///" + win.SimpleTimer.customAudioPath :
"chrome://simpletimer/content/sounds/" +
selectedAudioFile + ".wav";
try {
var sound = Components.classes["@mozilla.org/sound;1"].
createInstance(Components.interfaces.nsISound);
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(soundURL, null, null);
sound.init();
sound.play(uri);
}
catch (e) {
alert(strbundle.getString("alert.error.audio.failed") +
"\n" + e.name);
}
}
},
// Called when user closes Timer Completed dialog, or Kill Audio Alarm menuitem selected.
pauseHtmlAudio: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
// Seems odd there is no stop function.
if ( win.SimpleTimer.audioObject && !win.SimpleTimer.audioObject.ended ) {
win.SimpleTimer.audioObject.pause();
win.document.getElementById("simtim-mitemKillHtmlAudio").disabled = true;
}
}
},
// Called when html5 audio file metadata has loaded.
// this.audioDuration used when repeating audio alarm, see displayTimerCompleteDlg().
audioLoadedMetaData: function(evt) {
this.debug("Simple Timer: ogg metadata loaded");
var audioObject = evt.target;
this.audioDuration = audioObject.duration;
this.debug("Simple Timer: ogg duration (secs): " + this.audioDuration);
},
// Called when html5 audio file playback ends.
audioPlaybackEnded: function(evt) {
this.debug("Simple Timer: ogg ended");
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
win.document.getElementById("simtim-mitemKillHtmlAudio").disabled = true;
}
},
// Called when html5 audio file playback paused.
// Debugging.
audioPlaybackPause: function(evt) {
this.debug("Simple Timer: ogg paused");
},
// Called when html5 audio file playback aborts.
// Debugging.
audioPlaybackAbort: function(evt) {
this.debug("Simple Timer: ogg aborted");
},
// Called when an error occurs during html5 audio file playback.
audioPlaybackError: function(evt) {
var strbundle = document.getElementById("simtim-strings");
var audioObject = evt.target;
var errorCode;
switch ( audioObject.error.code ) {
case 1:
errorCode = strbundle.getString("alert.error.html.error.aborted");
break;
case 2:
errorCode = strbundle.getString("alert.error.html.error.network");
break;
case 3:
errorCode = strbundle.getString("alert.error.html.error.decode");
break;
case 4:
errorCode = strbundle.getString("alert.error.html.error.not.supported");
break;
default:
errorCode = strbundle.getString("alert.error.html.error.unknown");
}
alert(strbundle.getString(errorCode));
},
// Display the timer completed dialog.
displayTimerCompleteDlg: function(mode) {
var strbundle = document.getElementById("simtim-strings");
// Set the message to display in dialog.
if ( this.audioNotify &&
!this.repeatAudioId &&
this.repeatMax > 0 &&
this.repeatAudio !== 0 ) {
// Only one audio timer no matter how many
// dialogs stack up.
// repeatAudio > 0 is minutes, < 0 is seconds.
var repeatAudioInterval = this.repeatAudio > 0 ?
this.repeatAudio * 60 * 1000 :
this.repeatAudio * -1 * 1000;
if ( this.usingAudioObject ) {
var audioDurationMilli = ( Math.ceil(this.audioDuration) + 1 ) * 1000;
if ( audioDurationMilli > repeatAudioInterval ) {
repeatAudioInterval = audioDurationMilli;
}
}
var repeatMaxInterval = this.repeatAudio > 0 ?
repeatAudioInterval * this.repeatMax + ( 30 * 1000 ) :
repeatAudioInterval * this.repeatMax + 500;
this.repeatAudioId =
setInterval("SimpleTimer.playSound(SimpleTimer.audioFile);",
repeatAudioInterval);
this.repeatMaxId =
setTimeout("SimpleTimer.clearTimerCompleteInterval();",
repeatMaxInterval);
}
// It seems odd that the dlgs directly overlap with
// no offset. So manually set offset.
try {
var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].
getService(Components.interfaces.nsIWindowMediator);
var enumerator = wm.getEnumerator("dialog:timerComplete");
while(enumerator.hasMoreElements()) {
var win = enumerator.getNext();
win.moveBy(-26, -26);
}
}
catch (e) {
alert(strbundle.getString("alert.error.dialog.failed") +
"\n" + e.name);
}
var msg = this.getDisplayMsg(mode);
var url = this.getDisplayUrl(mode);
var params = {inn: {msg: msg, url: url}, out: null};
// Non-modal.
window.openDialog(
"chrome://simpletimer/content/simpletimer-timerComplete.xul",
"",
"centerscreen, chrome, resizable, dependent",
params);
},
// Called when user closes Timer Completed dialog,
// or when max repeat audio cycles reached.
clearTimerCompleteInterval: function() {
if ( this.repeatAudioId ) {
clearInterval(this.repeatAudioId);
this.repeatAudioId = 0;
}
if ( this.repeatMaxId ) {
clearTimeout(this.repeatMaxId);
this.repeatMaxId = 0;
}
},
// Get the message to be displayed.
getDisplayMsg: function(mode) {
var strbundle = document.getElementById("simtim-strings");
// If user supplied a description, use it.
if ( mode === "notify" ) {
return this.notifyTable[this.currNotifyIndex].description ?
this.notifyTable[this.currNotifyIndex].description :
strbundle.getString("alert.notification.notify.complete");
}
else {
return this.countdownTable[mode].description ?
this.countdownTable[mode].description :
strbundle.getString("alert.notification.timer.complete");
}
},
// Get the URL, if any, to be displayed.
getDisplayUrl: function(mode) {
if ( mode === "notify" ) {
return this.notifyTable[this.currNotifyIndex].url ?
this.notifyTable[this.currNotifyIndex].url : "";
}
else {
return "";
}
},
// Called when user selects Count Up menuitem.
countUp: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
win.SimpleTimer.cancelTimerInProgress();
if ( win.SimpleTimer.countdowns ) {
win.SimpleTimer.countdownTable = [];
win.SimpleTimer.setCountdownTable();
win.SimpleTimerCountdown.updateCountdownPref();
}
win.SimpleTimer.changeBarMode("wake");
win.SimpleTimer.displayTime(0, 0, 0);
win.SimpleTimer.startTime = new Date();
win.SimpleTimer.countupInProgress = true;
win.SimpleTimer.timerID = win.setInterval(win.SimpleTimer.increaseTimer, 1000);
}
},
// Increment and display the timer.
increaseTimer: function() {
SimpleTimer.calcElapsedTime();
SimpleTimer.displayTime(SimpleTimer.elapsedHours, SimpleTimer.elapsedMins, SimpleTimer.elapsedSecs);
},
// Called when user selects Pause/Resume menuitem, or via middle-click on panel.
pauseResume: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
if ( this.countdownInProgress || this.countupInProgress ) {
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
if ( win.SimpleTimer.countupInProgress || win.SimpleTimer.countdownInProgress ) {
if ( win.SimpleTimer.paused ) {
win.SimpleTimer.resume();
}
else {
win.SimpleTimer.pause();
}
}
}
}
},
// Stop the current count up/count down, leave the bar display intact.
pause: function() {
if ( SimpleTimer.countupInProgress ) {
clearInterval(SimpleTimer.timerID);
SimpleTimer.timerID = 0;
SimpleTimer.startTime = null;
}
else {
// Reset timeMilli to the remaining time. When resumed, it will be
// processed as if a new timer (although the display time is unchanged).
SimpleTimer.countdownTable[0].timeMilli = SimpleTimer.remainingMilli;
SimpleTimer.countdownTable[0].timeFinishedMilli = 0;
SimpleTimer.countdownTable[0].paused = true;
// Update countdowns pref.
SimpleTimer.setCountdownTable();
SimpleTimerCountdown.updateCountdownPref();
}
SimpleTimer.paused = true;
},
// Resume the current count up/count down.
resume: function() {
SimpleTimer.paused = false;
if ( SimpleTimer.countupInProgress ) {
SimpleTimer.startTime = new Date(new Date().getTime() - SimpleTimer.elapsedMilli);
SimpleTimer.timerID = setInterval("SimpleTimer.increaseTimer()", 1000);
}
else {
this.cancelTimerInProgress();
SimpleTimer.countdownMilli = SimpleTimer.countdownTable[0].timeMilli;
SimpleTimer.countdownTable[0].paused = false;
// Countdowns pref.updated there.
SimpleTimer.setCountdownTimer();
}
},
// Called when user selects Stop/Reset menuitem, or via double-click on panel.
stop: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
while ( enumerator.hasMoreElements() ) {
var win = enumerator.getNext();
win.SimpleTimer.cancelTimerInProgress();
if ( win.SimpleTimer.countdownTable.length > 0 ) {
win.SimpleTimer.countdownTable.shift();
win.SimpleTimer.setCountdownTimer();
}
else {
win.SimpleTimer.changeBarMode("sleep");
}
}
},
// Displays the options dialog.
displayOptionsDlg: function() {
window.openDialog(
"chrome://simpletimer/content/simpletimer-options.xul",
"",
"centerscreen, toolbar, titlebar, chrome, resizable, dependent");
},
// Displays the Event Log dialog.
displayEventLogDlg: function() {
window.openDialog(
"chrome://simpletimer/content/simpletimer-eventLog.xul",
"",
"centerscreen, chrome, resizable, dependent");
},
// Displays the About dialog.
displayAboutDlg: function() {
window.openDialog(
"chrome://simpletimer/content/simpletimer-about.xul",
"",
"centerscreen, chrome, resizable, dependent");
},
// Generic open page in new tab.
// This function developed by Devon Jensen, from Download Statusbar.
openLink: function(aPage) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var win = wmed.getMostRecentWindow("navigator:browser");
if ( !win ) {
win = window.openDialog(
"chrome://browser/content/browser.xul",
"_blank",
"chrome,all,dialog=no",
aPage, null, null);
}
else {
var content = win.document.getElementById("content");
content.selectedTab = content.addTab(aPage);
}
},
// Open page in new tab.
// Use this to auto-open (completed notification) a page for each browser window open.
autoOpenLink: function(aPage) {
var content = document.getElementById("content");
content.selectedTab = content.addTab(aPage);
},
// Check if other browser windows open.
// Don't display the startup slider alerts if true.
otherBrowserWindowOpen: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
if ( enumerator.hasMoreElements() ) {
// Skip oldest window.
enumerator.getNext();
}
return enumerator.hasMoreElements();
},
// If other browser windows open, check if countup in progress.
// If so, mirror it.
countupInOtherBrowserWindow: function() {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var enumerator = wmed.getEnumerator("navigator:browser");
if ( enumerator.hasMoreElements() ) {
// Oldest first.
var win = enumerator.getNext();
if ( win.SimpleTimer.countupInProgress ) {
this.countupInProgress = true;
this.paused = win.SimpleTimer.paused;
this.changeBarMode("wake");
if ( this.paused ) {
this.elapsedMilli = win.SimpleTimer.elapsedMilli;
this.elapsedHours = win.SimpleTimer.elapsedHours;
this.elapsedMins = win.SimpleTimer.elapsedMins;
this.elapsedSecs = win.SimpleTimer.elapsedSecs;
this.displayTime(this.elapsedHours, this.elapsedMins, this.elapsedSecs);
}
else {
this.startTime = win.SimpleTimer.startTime;
this.timerID = window.setInterval(window.SimpleTimer.increaseTimer, 1000);
}
return true;
}
}
return false;
},
// Deep copy an array of objects.
deepCopy: function(inArray) {
var json = Components.classes["@mozilla.org/dom/json;1"].
createInstance(Components.interfaces.nsIJSON);
var outArray = [];
for ( var i in inArray ) {
outArray.push(json.decode(json.encode(inArray[i])));
}
return outArray;
},
// Dump to console.
dumpNotificationObject: function(notification) {
this.debug("\nNotification data:");
this.debug("Completed?: " + notification.completed);
this.debug("Recurring?: " + notification.recurring);
this.debug("Notification time: " + notification.timeDisplay);
this.debug("Description: " + notification.description);
this.debug("Notification time (24 hr format): " + notification.time24hr);
this.debug("Notification time (millisecs): " + notification.timeMilli);
this.debug("Notification day: " + notification.day);
this.debug("Url: " + notification.url);
},
// Dump to console.
dumpCountdownObject: function(countdownTimer) {
this.debug("\nCountdown timer data:");
this.debug("Countdown Time for Display: " + countdownTimer.timeDisplay);
this.debug("Recurring?: " + countdownTimer.recurring);
this.debug("Description: " + countdownTimer.description);
this.debug("Countdown time (millisecs): " + countdownTimer.timeMilli);
this.debug("Countdown finish time (millisecs): " + countdownTimer.timeFinishedMilli);
},
// Debug messages to console.
debug: function(aMsg) {
setTimeout(function() { throw new Error("[debug] " + aMsg); }, 0);
}
};